package com.usbserial.core;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.Build;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Encapsulates the Android USB permission flow so callers can request permission and receive callbacks.
 */
class UsbPermissionHandler {

    static final String ACTION_USB_PERMISSION = "com.usbserial.core.USB_PERMISSION";

    private final Context context;
    private final UsbManager usbManager;
    private final ConcurrentMap<Integer, UsbPermissionCallback> callbacks = new ConcurrentHashMap<>();

    private final BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (!ACTION_USB_PERMISSION.equals(intent.getAction())) {
                return;
            }
            UsbDevice device;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE, UsbDevice.class);
            } else {
                device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
            }
            if (device == null) {
                return;
            }
            UsbPermissionCallback callback = callbacks.remove(device.getDeviceId());
            if (callback == null) {
                return;
            }
            boolean granted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
            if (granted) {
                callback.onPermissionGranted(device);
            } else {
                callback.onPermissionDenied(device);
            }
        }
    };

    UsbPermissionHandler(Context context, UsbManager usbManager) {
        this.context = context.getApplicationContext();
        this.usbManager = usbManager;
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            this.context.registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED);
        } else {
            this.context.registerReceiver(receiver, filter);
        }
    }

    void requestPermission(UsbDevice device, UsbPermissionCallback callback) {
        callbacks.put(device.getDeviceId(), callback);
        PendingIntent permissionIntent = PendingIntent.getBroadcast(context, 0, buildPermissionIntent(), derivePendingIntentFlags());
        usbManager.requestPermission(device, permissionIntent);
    }

    boolean hasPermission(UsbDevice device) {
        return usbManager.hasPermission(device);
    }

    void dispose() {
        context.unregisterReceiver(receiver);
        callbacks.clear();
    }

    private Intent buildPermissionIntent() {
        Intent intent = new Intent(ACTION_USB_PERMISSION);
        try {
            Class<?> activityThread = Class.forName("android.app.ActivityThread");
            Method method = activityThread.getDeclaredMethod("currentPackageName");
            Object packageName = method.invoke(activityThread);
            if (packageName instanceof String) {
                intent.setPackage((String) packageName);
            }
        } catch (Exception ignored) {
            // Package scoping is best effort only.
        }
        return intent;
    }

    private int derivePendingIntentFlags() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            return PendingIntent.FLAG_MUTABLE;
        }
        return 0;
    }
}
